﻿#include "FITKGraphObjectSurface.h"

// VTK
#include <vtkProperty.h>
#include <vtkSmartPointer.h>
#include <vtkDataSet.h>
#include <vtkUnstructuredGrid.h>
#include <vtkSelectionNode.h>
#include <vtkSelection.h>
#include <vtkExtractSelection.h>
#include <vtkIdTypeArray.h>
#include <vtkPointData.h>
#include <vtkCellData.h>

// Graph
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"
#include "FITKGraphCommons.h"

// Data and manager
#include "FITK_Interface/FITKInterfaceModel/FITKMeshSurface.h"
#include "FITKSetSurfPolyProvider.h"

namespace Exchange
{
    FITKGraphObjectSurface::FITKGraphObjectSurface(AbaqusData::FITKDataCase* caseData, Interface::FITKMeshSurface* surface, QVariant details)
        : FITKGraphObjectHighlightBase(caseData, surface), m_instanceId(details.isValid() ? details.toInt() : -1)
    {
        // Return if the input is nullptr.
        if (!surface)
        {
            return;
        }

        // Initialize.
        init();

        // Generate the surface poly.
        bool flag = generatePolyData(surface);
        if (!flag)
        {
            return;
        }

        // Generate the graph actor.
        generateGraph();

        // Update the surface visibility. 
        updateVisibility();
    }

    FITKGraphObjectSurface::~FITKGraphObjectSurface()
    {
        // Delete extractor pointers.
        deleteVtkObj(m_selectNode);
        deleteVtkObj(m_section);
        deleteVtkObj(m_extractSelection);
        deleteVtkObj(m_idArray);
    }

    void FITKGraphObjectSurface::init()
    {
        Interface::FITKMeshSurface* surface = dynamic_cast<Interface::FITKMeshSurface*>(_dataObj);
        if (!surface)
        {
            return;
        }

        // Check the surface type.
        Interface::FITKMeshSurface::MeshSurfaceType type = surface->getMeshSurfaceType();
        if (type == Interface::MeshSurfaceType::SurNone)
        {
            return;
        }

        // Initialize the extractor.
        //@{
        m_selectNode = vtkSelectionNode::New();
        m_section = vtkSelection::New();
        m_extractSelection = vtkExtractSelection::New();
        m_selectNode->SetContentType(vtkSelectionNode::INDICES);
        m_section->AddNode(m_selectNode);
        m_extractSelection->SetInputData(1, m_section);

        m_idArray = vtkIdTypeArray::New();
        m_selectNode->SetSelectionList(m_idArray);
        //@}

        // Save the surface type.
        if (type == Interface::MeshSurfaceType::SurEle)
        {
            m_type = CellSurface;
            m_selectNode->SetFieldType(vtkSelectionNode::SelectionField::CELL);
        }
        else if (type == Interface::MeshSurfaceType::SurNode)
        {
            m_type = PointSurface;
            m_selectNode->SetFieldType(vtkSelectionNode::SelectionField::POINT);
        }

        // Create the surface poly data.
        m_gridData = vtkUnstructuredGrid::New();

        // Set the data to the filter.
        m_extractSelection->SetInputData(m_gridData);
    }

    void FITKGraphObjectSurface::update(bool forceUpdate)
    {
        // There is no need to update the surface graph object in normal case.
        if (forceUpdate)
        {
            Interface::FITKMeshSurface* surface = dynamic_cast<Interface::FITKMeshSurface*>(_dataObj);
            if (!surface)
            {
                return;
            }

            // Regenerate the poly data.
            generatePolyData(surface);
        }

        // Update the visibility.
        updateVisibility();
    }

    void FITKGraphObjectSurface::updateVisibility()
    {
        if (!m_gridData)
        {
            return;
        }

        QHash<int, bool> modelVisibilityHash;

        // Save the visibility first, and check if the visbility has changed at the same time.
        bool hasChanged = false;
        for (int modelId : m_modelIds)
        {
            bool parentVisible = getParentDataVisibility(modelId);
            bool oldVisibility = false;
            if (m_modelVisibilityHash.contains(modelId))
            {
                oldVisibility = m_modelVisibilityHash[modelId];
            }

            if (parentVisible != oldVisibility)
            {
                hasChanged = true;
            }

            modelVisibilityHash.insert(modelId, parentVisible);
        }

        // If the visibility hasn't changed, then return.
        if (!hasChanged && !m_modelVisibilityHash.isEmpty())
        {
            return;
        }

        vtkIntArray* modelIdArray{ nullptr };
        if (m_type == CellSurface)
        {
            modelIdArray = vtkIntArray::SafeDownCast(m_gridData->GetCellData()->GetArray(FITKSetSurfPolyProvider::getInstance()->getModelIdsArrayName()));
        }
        else
        {
            modelIdArray = vtkIntArray::SafeDownCast(m_gridData->GetPointData()->GetArray(FITKSetSurfPolyProvider::getInstance()->getModelIdsArrayName()));
        }

        if (!modelIdArray)
        {
            return;
        }

        // Reset the extractor's ids.
        resetVtkObj(m_idArray);

        // Update the extractor's ids.
        for (int i = 0; i < modelIdArray->GetNumberOfValues(); i++)
        {
            int modelId = modelIdArray->GetValue(i);
            if (modelVisibilityHash[modelId])
            {
                m_idArray->InsertNextValue(i);
            }
        }

        // Update the data.
        m_gridData->Modified();

        // Save the flags.
        m_modelVisibilityHash = modelVisibilityHash;
    }

    bool FITKGraphObjectSurface::generatePolyData(Interface::FITKMeshSurface* surface)
    {
        if (!surface || !m_gridData)
        {
            return false;
        }

        // Check the surface type.
        Interface::FITKMeshSurface::MeshSurfaceType type = surface->getMeshSurfaceType();
        if (type == Interface::MeshSurfaceType::SurNone)
        {
            return false;
        }

        // Get or generate surface data.
        vtkUnstructuredGrid* surfGrid = FITKSetSurfPolyProvider::getInstance()->getSurfaceGridData(surface->getDataObjectID(), m_modelIds, m_instanceId);
        if (!surfGrid)
        {
            return false;
        }

        // Copy a new one.
        m_gridData->DeepCopy(surfGrid);

        // Remove the normals data.
        FITKGraphCommons::RemoveCellNormals(m_gridData);

        return true;
    }

    void FITKGraphObjectSurface::generateGraph()
    {
        Interface::FITKMeshSurface* surface = dynamic_cast<Interface::FITKMeshSurface*>(_dataObj);
        if (!surface)
        {
            return;
        }

        // Check the surface type.
        Interface::FITKMeshSurface::MeshSurfaceType type = surface->getMeshSurfaceType();
        if (type == Interface::MeshSurfaceType::SurNone)
        {
            return;
        }

        // Initialize the actor.
        m_fActor = FITKGraphActor::New();
        m_fActor->setGraphObject(this);
        m_fActor->SetPickable(false);
        m_fActor->setInputConnection(m_extractSelection->GetOutputPort());
        addActor(m_fActor);

        // Initialize actor properties.
        initActorProperties(m_fActor, type == Interface::MeshSurfaceType::SurNode ? 1 : 2);
    }
}   // namespace Exchange